#include "main_form.h"
#include "ui_main_form.h"

#include <QDomDocument>
#include <QDomElement>
#include <QMessageBox>
#include <QListWidget>
#include <QListWidgetItem>

#include "bitwin/bitwin.h"

// Anonymous namespace for internal usage staff
namespace
{


// Iterate over the QListWidget items and obtain
// data of the given kind
template <typename T, int role=Qt::DisplayRole>
class ConstQListWidgetIterator
{
    const QListWidget* const _widget;
    int _pos;

public:

    typedef std::input_iterator_tag iterator_category;
    typedef int difference_type;
    typedef T value_type;
    typedef void pointer;
    typedef void reference;

    ConstQListWidgetIterator(
        const QListWidget* const widget_, const int pos=0
    ):
        _widget(widget_),
        _pos(
            (pos >= 0) && (pos <= widget_->count()) ?
                pos : widget_->count()
        )
    {}

    // HACK! The result s.b. of type "reference"
    const value_type operator*() const
    {
        if (QListWidgetItem* item = _widget->item(_pos))
            return item->data(role).value<T>();
        else
            return T();
    }

    ConstQListWidgetIterator& operator++()
    {
        ++_pos;
        return *this;
    }

    bool operator==(const ConstQListWidgetIterator& rhs) const
    {
        return (rhs._pos == _pos);
    }

    bool operator!=(const ConstQListWidgetIterator& rhs) const
    {
        return !(rhs == *this);
    }
};


// Convert the value of the given type to the 32-bits wide
// PGenericBitwise.

// Generic class template declaration
template <typename T>
class BitwiseQuint32Maker;

// The specialization for T=QString
template <>
class BitwiseQuint32Maker<QString>
{
public:
    BitWin::PGenericBitwise operator()(const QString& s) const
    {
        return BitWin::GenericBitwise::fromIntegral<quint32>(
            s.toUInt(0, 0)
        );
    }
};


} // Anonymous namespace end


MainForm::MainForm(QWidget* parent):
    QWidget(parent),
    _ui(new Ui::MainForm()),
    _model(0)
{
    _ui->setupUi(this);
    connect(
        _ui->btn_commit_format, SIGNAL(clicked()),
        this, SLOT(commitFormat())
    );
    connect(
        _ui->w_data, SIGNAL(itemChanged(QListWidgetItem*)),
        this, SLOT(dataChanged(QListWidgetItem*))
    );
    
    _ui->w_format->setFontFamily("Courier");
    
    _ui->w_format->setPlainText(
        tr(
            "<?xml version='1.0' encoding='UTF-8'?>\n"
            "<model>\n"
            "    <rows>\n"
            "        <word title=''>\n"
            "            <field title='Порт отправителя' bits='31:16'/>\n"
            "            <field title='Порт получателя' bits='15:0'/>\n"
            "        </word>\n"
            "        <word title='Порядковый номер'/>\n"
            "        <word title='Номер подтверждения'/>\n"
            "        <word title=''>\n"
            "            <field title='Длина заголовка' bits='31:28'/>\n"
            "            <field title='Неиспользуемое поле' bits='27:22'/>\n"
            "            <field title='URG' bits='21'/>\n"
            "            <field title='ACK' bits='20'/>\n"
            "            <field title='PSH' bits='19'/>\n"
            "            <field title='RST' bits='18'/>\n"
            "            <field title='SYN' bits='17'/>\n"
            "            <field title='FIN' bits='16'/>\n"
            "            <field title='Размер окна' bits='15:0'/>\n"
            "        </word>\n"
            "        <word title=''>\n"
            "            <field title='Контрольная сумма' bits='31:16'/>\n"
            "            <field title='Указатель на срочные данные' bits='15:0'/>\n"
            "        </word>\n"
            "    </rows>\n"
            "    <columns>\n"
            "        <bin title='Байт 3' bits='31:24'/>\n"
            "        <bin title='Байт 2' bits='23:16'/>\n"
            "        <bin title='Байт 1' bits='15:8'/>\n"
            "        <bin title='Байт 0' bits='7:0'/>\n"
            "    </columns>\n"
            "</model>\n"
        )
    );
    
    _ui->w_bitwin->setItemDelegate(new BitWin::Delegate(this));
    
    addNewItemToList("0xC0D4F00D");
    addNewItemToList("0xA0F1823D");
    addNewItemToList("0x11111111");
    addNewItemToList("0x22222222");
    addNewItemToList("0xFFFFFFFF");
    addNewItemToList();
    
    commitFormat();
}

MainForm::~MainForm()
{
    delete _ui;
}

void MainForm::commitData()
{
    if (_model)
        _model->commitData<BitwiseQuint32Maker>(
            ConstQListWidgetIterator<QString>(_ui->w_data),
            ConstQListWidgetIterator<QString>(
                _ui->w_data,
                // HACK! It is assumed, that count() is always >= 1
                _ui->w_data->count() - 1
            )
        );
}

void MainForm::commitFormat()
{
    QDomDocument doc;
    QString err_msg;
    int err_line;
    int err_column;
    
    if (
        !doc.setContent(
            _ui->w_format->toPlainText(),
            &err_msg, &err_line, &err_column
        )
    )
    {
        QMessageBox msg_box;
        msg_box.setText(
            tr("Ошибка разбора XML-документа в строке %1 колонке %2\n Диагноз:\n%3")
            .arg(err_line)
            .arg(err_column)
            .arg(err_msg)
        );
        msg_box.setIcon(QMessageBox::Critical);
        msg_box.exec();
    }
    else
    {
        BitWin::Model* new_model =
            BitWin::FromXml::makeModel(doc.documentElement())
        ;
        if (new_model)
        {
            new_model->setParent(this);
            BitWin::Model* old_model = _model;
            _ui->w_bitwin->setModel(new_model);
            _model = new_model;
            delete old_model;
            commitData();
        }
    }
}

void MainForm::dataChanged(QListWidgetItem* item)
{
    int row = _ui->w_data->row(item);
    if (row < 0)
        return;
    
    bool is_last = ((_ui->w_data->count() - 1) == row);
    bool is_empty =
        item->data(Qt::DisplayRole).toString().simplified().isEmpty()
    ;
    
    if (is_empty && (!is_last))
        delete item;
    else if ((!is_empty) && (is_last))
        addNewItemToList();
    
    commitData();
}

void MainForm::addNewItemToList(const QString& data)
{
    QListWidgetItem* new_item = new QListWidgetItem(data);
    new_item->setFlags(new_item->flags() | Qt::ItemIsEditable);
    _ui->w_data->addItem(new_item);
}
